﻿using LPBToolsLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StationSer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;

/*
工作站管理基础对象
IDatasBus：      多通讯总线支持专用的接口
BusTypes：       多通讯总线支持专用总线类型枚举
StationPLCType： 系统支持的PLC类型

 */
namespace StationNF
{
    public delegate void reFlashRegValuesCallBack( int iRegStar, ushort[] tNewRegValues);
    public delegate int writeToPLCCallBack(byte[] tNewRegValues);

    /// <summary>
    /// 通讯总线接口
    /// 仅用于多总线类型系统，单一总线系统中仅做参考，不使用
    /// </summary>
    public interface IDatasBus
    {
        BusTypes BusType { get; }
        bool BusStatus();
        void CloseAll();
        void Start();
        string BusInfo { get; }
        event BusRecCallBack OnRecPacket;
        T getBus<T>();
        int putStation(PLCStation tNewStation);
    }

    /// <summary>
    /// 通讯总线类型
    /// </summary>
    public enum BusTypes
    {
        None = 0,
        SerialPort,
        UDPPassive,
        SPRTUModbus,
        TCPRTUModbus,
    }
       
    /// <summary>
    /// 工作站对象接口
    /// </summary>
    public interface IFStation
    {
        int StationType { get; }
        DateTime LastActiveTime { get; }
        /// <summary>
        /// 工作站ID[32字符，16个word]
        /// </summary>
        string StaID { get; }
        /// <summary>
        /// 是否获得新的寄存器数据，读取后变成false
        /// </summary>
        bool hasFlash { get; }
        /// <summary>
        /// PLC寄存器起始地址
        /// </summary>
        int StationRegStar { get; }
        /// <summary>
        /// PLC寄存器数量
        /// </summary>
        int StationRegCount { get; }
        /// <summary>
        /// PLC所有寄存器镜像值
        /// </summary>
        ushort[] RegValues { get; }
        //void reFlashRegValues(int iRegStar, ushort[] tNewRegValues);
        /// <summary>
        /// 收到Option的处理接口
        /// </summary>
        /// <param name="sender">通讯控件对象</param>
        /// <param name="OptionDatas">收到的Option数据</param>
        /// <param name="ResponOpt">返回应答命令数据</param>
        void RecOption(byte[] OptionDatas, out byte[] ResponOpt);
        /// <summary>
        /// 设置向工作站应答或下达的命令
        /// </summary>
        /// <param name="tOption"></param>
        void setResponCommand(DExOptionBase tOption);
        /// <summary>
        /// 提取工作站的描述信息
        /// </summary>
        /// <returns></returns>
        Dictionary<string, object> getStationStatus();
        void CloseAll();
        int setPLCRunOnOff(int YPortIdx,bool value);
    }

    /// <summary>
    /// 工作站对象基础类
    /// </summary>
    public abstract class StationBase :ContainerBase
    {
        public object Tag { get; set; } = null;
        public Guid StationGUID { get; }=Guid.NewGuid();
        /// <summary>
        /// 仅用于从JSON数据还原节点信息用
        /// </summary>
        protected string ParentBusInfoStr { get; set; } = "";

        [DisplayName("工作站类型")]
        public virtual int StationType { get;  set; } = 0;
        public Recorder_Error SysErr { get; set; } = null;
        public Recorder_RunLog RunLog { get; set; } = null;
        /// <summary>
        /// 最后活动时间
        /// </summary>
        public DateTime LastActiveTime { get; protected set; } = DateTime.Now;
        /// <summary>
        /// 能够持续的时间长度
        /// </summary>
        public int Interval_ms { get; protected set; } = 5000;
        /// <summary>
        /// 对象是否已经超时
        /// </summary>
        public bool IsTimeOut
        {
            get
            {
                return ((DateTime.Now - LastActiveTime).TotalMilliseconds > Interval_ms);
            }
        }

        private static string MutexKeyStr = "StationBase" + Guid.NewGuid().ToString();
        private Mutex tMutex { get; } = new Mutex(false, MutexKeyStr);

        public void CloseAll()
        {
            LastActiveTime = DateTime.MinValue;
            StaID = "";
        }

        [DisplayName("工作站寄存器起始地址")]
        public virtual int StationRegStar { get; protected set; } = 0;

        [DisplayName("工作站寄存器数量")]
        public int StationRegCount
        {
            get { return tStationRegMirror.Count; }
            protected set
            {
                if (value >= 0)
                {
                    tMutex.WaitOne();
                    try
                    {
                        tStationRegMirror.Clear();
                        if (value > 0)
                            tStationRegMirror.AddRange(new ushort[value]);
                    }
                    finally
                    {
                        tMutex.ReleaseMutex();
                    }
                }
            }
        }
        protected List<ushort> tStationRegMirror { get; } = new List<ushort>();
        public ushort[] RegValues { get { return tStationRegMirror.ToArray(); } }
        [DisplayName("工作站ID")]
        public string StaID { get; protected set; } = "";
        protected bool iFlash = false;
        public bool hasFlash
        {
            get
            {
                bool Tmp = iFlash;
                iFlash = false;
                return Tmp;
            }
        } 
        /// <summary>
        /// 用新收到的上传数据，更新寄存器镜像中的数据，同时更新本对象最后活跃时间
        /// </summary>
        /// <param name="iRegStar">更新的起始地址，使用全寄存器绝对地址，从0开始</param>
        /// <param name="tNewRegValues"></param>
        public void reFlashRegValues(int iRegStar, ushort[] tNewRegValues)
        {
            LastActiveTime = DateTime.Now;
            if ((tNewRegValues != null) && (iRegStar >= StationRegStar)
                && (iRegStar < StationRegCount + StationRegStar)
                && (iRegStar + tNewRegValues.Length <= StationRegCount + StationRegStar))
            {
                tMutex.WaitOne();
                try
                {
                    for (int i = 0; i < tNewRegValues.Length; i++)
                        tStationRegMirror[iRegStar + i - StationRegStar] = tNewRegValues[i];
                }
                finally
                {
                    tMutex.ReleaseMutex();
                }
                iFlash = true;
            }
        }
        
        /// <summary>
        /// 提取寄存器镜像中指定地址的值，使用绝对地址，从0开始；
        /// </summary>
        /// <param name="iRegAddress"></param>
        /// <param name="RegValue">成功则返回寄存器的值</param>
        /// <returns>地址正确返回True</returns>
        protected bool getRegValue(int iRegAddress,out ushort RegValue)
        {
            RegValue = 0;
            if ((iRegAddress>=StationRegStar)&&(iRegAddress< StationRegStar+StationRegCount))
            {
                RegValue = tStationRegMirror[iRegAddress - StationRegStar];
                return true;
            }
            return false;
        }

        public ushort getRegValueABS(ushort iRegAddress)
        {
            if ((iRegAddress >= StationRegStar) && (iRegAddress < StationRegStar + StationRegCount))
            {
                return tStationRegMirror[iRegAddress - StationRegStar];
            }
            return 0;
        }
        public StationBase(string stationID)
        {
            StationRegCount = 0;
            StaID = stationID;
        }

        /// <summary>
        /// 生成的数据能够直接进行数据刷新
        /// </summary>
        /// <param name="StationInfo"></param>
        /// <returns></returns>
        public abstract bool getStationBaseInfo(out Dictionary<string, object> StationInfo);

        public abstract Dictionary<string, object> getStationStatus();
               
    }

    /// <summary>
    /// PLC工作站基础类型对象
    /// 仅作为初始解析命令使用
    /// </summary>
    public class PLCStation : StationBase, IFStation
    {
        /// <summary>
        /// 发生写入寄存器数值操作的回调函数，用于客户端处理发送写入寄存器操作
        /// </summary>
        public writeToPLCCallBack OnWriteRegValues;
        /// <summary>
        /// PLC工作站类型ID，存放数据库中PLC类型ID，从1开始实际PLC类型
        /// </summary>
       // public override StationPLCType StationType { get; protected set; } = StationPLCType.ModbusPLC;

        //public StationPLCType StaType { get { return StationType; } }
        /// <summary>
        /// 创建空的工作站对象，仅用于测试
        /// </summary>
        /// <param name="stationID"></param>
        /// <param name="DestRegCount"></param>
        /// <param name="DestRegStar"></param>
        public PLCStation(string stationID,  int DestRegCount = 0, int DestRegStar = 0) : base(stationID)
        {
            StationRegCount = DestRegCount;
            StationRegStar = DestRegStar;
        }
        /// <summary>
        /// 根据收到的Option创建PLC工作站
        /// 注意：该方式创建的PLC镜像中，寄存器起始地址就是Option的起始地址
        /// </summary>
        /// <param name="stationID"></param>
        /// <param name="UploadRegValue"></param>
        public PLCStation(string stationID, DExOptionBase UploadRegValue) : base(stationID)
        {
            if (UploadRegValue != null)
            {
                RunLog = UploadRegValue.RunLog;
                SysErr = UploadRegValue.SysErr;
            }
            if ((UploadRegValue != null)
                && (UploadRegValue.OptCommand == DExOpt.Opt_UploadRegValue_0x03))
            {
                DExOption_UploadRegValue_0x03 tUploadRegValue = DExOption_UploadRegValue_0x03.createOption(UploadRegValue);
                if ((tUploadRegValue.UploadReadRegCount > 0)
                && (tUploadRegValue.UploadReadRegStarAddr >= 0))
                {
                    StationRegCount = tUploadRegValue.UploadReadRegCount;
                    StationRegStar = tUploadRegValue.UploadReadRegStarAddr;
                    reFlashRegByOption(tUploadRegValue);
                }
            }
        }

        /// <summary>
        /// 从JSON数据，创建新的工作站信息
        /// 注意：所有JSON属性名称必须与工作站实际属性名称相符
        /// 输入的JSON可以不包括RegValues
        /// </summary>
        /// <param name="stationID"></param>
        /// <param name="StationInfo"></param>
        public PLCStation(string stationID, Dictionary<string, object> StationInfo) : this(stationID)
        {
            int iStaRegCount = 0;
            int iStaRegStar = 0;
            if ((StationInfo != null)
                && StationInfo.ContainsKey("StationRegStar")
                && int.TryParse(StationInfo["StationRegStar"].ToString(), out iStaRegStar)
                && StationInfo.ContainsKey("StationRegCount")
                && int.TryParse(StationInfo["StationRegCount"].ToString(), out iStaRegCount)
                && (iStaRegCount > 0)
                && StationInfo.ContainsKey("StaID")
                && stationID.Equals(StationInfo["StaID"].ToString()))
            {
                StationRegStar = iStaRegStar;
                StationRegCount = iStaRegCount;

                if (StationInfo.ContainsKey("StaID")
                    && (!string.IsNullOrEmpty(StationInfo["StaID"].ToString()))
                    && (StationInfo["StaID"].ToString().Length == 32))
                {
                    StaID = StationInfo["StaID"].ToString();
                }
                if (StationInfo.ContainsKey("ParentBusInfo"))
                    ParentBusInfoStr = StationInfo["ParentBusInfo"].ToString();
                if (StationInfo.ContainsKey("RegValues"))
                {
                    List<ushort> tRegValues = null;
                    string sType = StationInfo["RegValues"].GetType().ToString();
                    if (sType.Equals("Newtonsoft.Json.Linq.JArray"))
                        tRegValues = JsonConvert.DeserializeObject<List<ushort>>(StationInfo["RegValues"].ToString());
                    else
                        tRegValues = (List<ushort>)StationInfo["RegValues"];
                    if ((tRegValues != null) && (tRegValues.Count == iStaRegCount))
                        reFlashRegValues(StationRegStar, tRegValues.ToArray());
                }
                DateTime tDateTime;
                if (StationInfo.ContainsKey("LastActiveTime")
                    && DateTime.TryParse(StationInfo["LastActiveTime"].ToString(), out tDateTime))
                    LastActiveTime = tDateTime;
            }
        }


        protected void reFlashRegByOption(DExOptionBase tDOption)
        {
            LastActiveTime = DateTime.Now;
            if (tDOption != null)
            {
                if (tDOption.OptCommand == DExOpt.Opt_DeclarePLCType_0x01)
                {
                    DExOption_DeclarePLCType_0x01 tOpt01 = DExOption_DeclarePLCType_0x01.createOption(tDOption);
                    if (tOpt01 != null)
                    {
                        StationType = tOpt01.PLCRType;
                        StationRegStar = tOpt01.PLCRegStarAddr;
                        // 此处不可修改寄存器数量信息，否则导致寄存器数据清零
                        //StationRegCount = tOpt01.PLCRegCount;  // 该语句会重建寄存器缓冲，造成数据丢失
                        // 该命令不会触发数据更新
                    }
                }
                if (tDOption.OptCommand == DExOpt.Opt_UploadRegValue_0x03)
                {
                    DExOption_UploadRegValue_0x03 tOpt03 = DExOption_UploadRegValue_0x03.createOption(tDOption);
                    if ((tOpt03 != null)
                        && (tOpt03.UploadReadRegCount > 0)
                        && (tOpt03.UploadReadRegCount == tOpt03.UploadReadRegValues.Length))
                    {
                        if (tOpt03.UploadReadRegStarAddr != StationRegStar)
                            StationRegStar = tOpt03.UploadReadRegStarAddr;
                        if (tOpt03.UploadReadRegCount != StationRegCount)
                            StationRegCount = tOpt03.UploadReadRegCount;
                        ushort[] tNewRegValues = tOpt03.UploadReadRegValues;
                        reFlashRegValues(tOpt03.UploadReadRegStarAddr, tNewRegValues);
                    }
                }
            }
        }

        /// <summary>
        /// PLC工作站接受下位机上传的命令接口，通过通讯管道的setStation函数设置到OnRecOptioin事件中。
        /// 注意：该函数是在通讯管道的线程中执行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="OptionDatas"></param>
        /// <param name="ResponOpt"></param>
        public virtual void RecOption(byte[] OptionDatas, out byte[] ResponOpt)
        {
            ResponOpt = null;
            if ((OptionDatas != null) && (OptionDatas.Length >= 4))
            {
                DExOptionBase tDOption = new DExOptionBase(OptionDatas);
                while ((tDOption != null) && tDOption.OptReady)
                {
                    reFlashRegByOption(tDOption);
                    tDOption = tDOption.OptionOfNext();
                }
            }
            RunLog?.setRecord(new Record(toDetail()));
           
            // 在应答中追加需要下达的命令数据
            if (tResponOption != null)
            {
                ResponOpt = tResponOption.OptionDatas;
                tResponOption = null;
            }
            LastActiveTime = DateTime.Now;
        }
        private DExOptionBase tResponOption { get; set; } = null;
        private byte[] tResponOptDatas { get; set; } = null;
        public void setResponCommand(DExOptionBase tOption)
        {
            tResponOption = tOption;
            tResponOptDatas = tOption.OptionDatas;
        }

        /// <summary>
        /// 生成的数据能够直接进行数据刷新
        /// </summary>
        /// <param name="StationInfo"></param>
        /// <returns></returns>
        public override bool getStationBaseInfo(out Dictionary<string, object> StationInfo)
        {
            StationInfo = new Dictionary<string, object>();
            if (this.toDetail(out StationInfo))
            {
                if (StationInfo.ContainsKey("LastActiveTime"))
                {
                    StationInfo["LastActiveTime"] = this.LastActiveTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
                }
                StationInfo.Add("RegValues", tStationRegMirror);
                return true;
            }
            return false;
        }


        /// <summary>
        /// 仅返回工作站实际各通道、端口的运行值。
        /// 该函数仅用于继承后的重载
        /// </summary>
        /// <returns></returns>
        public override Dictionary<string, object> getStationStatus()
        {
            Dictionary<string, object> tStatus = new Dictionary<string, object>();
            tStatus.Add("StaID", StaID);
            tStatus.Add("LastActiveTime", LastActiveTime.ToString("HH:mm:ss.fff"));
            tStatus.Add("IsTimeOut", IsTimeOut);
            tStatus.Add("StationType", StationType);
            tStatus.Add("StationRegStar", StationRegStar);
            tStatus.Add("StationRegCount", StationRegCount);
            return tStatus;
        }
        /// <summary>
        /// 使用指定的时间作为数据最后活跃时间，主要用于客户端同步寄存器；
        /// </summary>
        /// <param name="iRegStar"></param>
        /// <param name="tNewRegValues"></param>
        /// <param name="sLastActiveTime">特定的时间，可能是从服务上获得的PLC更新时间。如果输入的时间字符串格式不合法，就会使用函数运行的时间作为最后活跃时间</param>
        public void reFlashRegValues(int iRegStar, ushort[] tNewRegValues, string sLastActiveTime)
        {
            reFlashRegValues(iRegStar, tNewRegValues);
            DateTime tDestDT = DateTime.Now;
            if (DateTime.TryParse(sLastActiveTime, out tDestDT))
                LastActiveTime = tDestDT;
        }

        public int setPLCRunOnOff(int YPortIdx, bool value)
        {
            int iResult = 0;
            DExOption_DownLoadWriteOn_0x05 tOptWrite0x05 = new DExOption_DownLoadWriteOn_0x05();
            tOptWrite0x05.YIndex = (byte)YPortIdx;
            tOptWrite0x05.Value = value;
            if (OnWriteRegValues != null)
                OnWriteRegValues(tOptWrite0x05.getRTxDatas());
            return iResult;
        }
    }


    
}